package com.caoji.serach.service.impl;

import com.caoji.advice.lyExcption;
import com.caoji.client.ItemClient;
import com.caoji.dto.PageDTO;
import com.caoji.entity.SkuDTO;
import com.caoji.entity.SpecParamDTO;
import com.caoji.entity.SpuDTO;
import com.caoji.serach.Repository.GoodsRepository;
import com.caoji.serach.entity.Goods;
import com.caoji.serach.entity.SearchParamDTO;
import com.caoji.serach.service.EsService;
import com.leyou.starter.elastic.entity.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.Nested;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import reactor.core.publisher.Mono;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description： User: caoji
 * Date: 2020/12/13
 * Time: 10:25
 */
@Slf4j
@Service
public class EsServiceimpl implements EsService {

    @Autowired
    GoodsRepository repository;

    @Autowired
    ItemClient itemClient;

    @Override
    public void createIndexAndMapping() {
        try {
            repository.deleteIndex();
        } catch (Exception e) {
            log.info("删除失败");

        }
        repository.createIndex("{\n" +
                "  \"settings\": {\n" +
                "    \"analysis\": {\n" +
                "      \"analyzer\": {\n" +
                "        \"my_pinyin\": {\n" +
                "          \"tokenizer\": \"ik_smart\",\n" +
                "          \"filter\": [\n" +
                "            \"py\"\n" +
                "          ]\n" +
                "        }\n" +
                "      },\n" +
                "      \"filter\": {\n" +
                "        \"py\": {\n" +
                "\t\t  \"type\": \"pinyin\",\n" +
                "          \"keep_full_pinyin\": true,\n" +
                "          \"keep_joined_full_pinyin\": true,\n" +
                "          \"keep_original\": true,\n" +
                "          \"limit_first_letter_length\": 16,\n" +
                "          \"remove_duplicated_term\": true\n" +
                "        }\n" +
                "      }\n" +
                "    }\n" +
                "  },\n" +
                "  \"mappings\": {\n" +
                "    \"properties\": {\n" +
                "      \"id\": {\n" +
                "        \"type\": \"keyword\"\n" +
                "      },\n" +
                "      \"suggestion\": {\n" +
                "        \"type\": \"completion\",\n" +
                "        \"analyzer\": \"my_pinyin\",\n" +
                "        \"search_analyzer\": \"ik_smart\"\n" +
                "      },\n" +
                "      \"title\":{\n" +
                "        \"type\": \"text\",\n" +
                "        \"analyzer\": \"my_pinyin\",\n" +
                "        \"search_analyzer\": \"ik_smart\"\n" +
                "      },\n" +
                "      \"image\":{\n" +
                "        \"type\": \"keyword\",\n" +
                "        \"index\": false\n" +
                "      },\n" +
                "      \"updateTime\":{\n" +
                "        \"type\": \"date\"\n" +
                "      },\n" +
                "      \"specs\":{\n" +
                "        \"type\": \"nested\",\n" +
                "        \"properties\": {\n" +
                "          \"name\":{\"type\": \"keyword\" },\n" +
                "          \"value\":{\"type\": \"keyword\" }\n" +
                "        }\n" +
                "      }\n" +
                "    }\n" +
                "  }\n" +
                "}");
    }

    @Override
    public void loadData() {
        int page=1,size=100;
        while (true){
            PageDTO<SpuDTO> spuDto = itemClient.querySpuByPage(page, size, true, null, null, null);
            List<SpuDTO> items = spuDto.getItems();
            List<Goods> goods = items.stream().map(this::buildgoods).collect(Collectors.toList());
            repository.saveAll(goods);
            log.info("导入第几页开始"+page);
            page++;
            Long totalPage = spuDto.getTotalPage();
            if (page>totalPage){
                break;
            }
        }

    }



    public  Goods buildgoods(SpuDTO spuDTO){
        //自动补全字段
        String gooodname = spuDTO.getName();
        String brandName = org.apache.commons.lang3.StringUtils.substringBeforeLast(spuDTO.getBrandName(),"（");
        String categoryName = org.apache.commons.lang3.StringUtils.substringAfterLast(spuDTO.getCategoryName(),"/");
        List<String> suggrsdt=new ArrayList<>();
        suggrsdt.add(gooodname+" "+brandName);
        suggrsdt.add(gooodname+" "+categoryName);
        suggrsdt.add(brandName+" "+categoryName);
        suggrsdt.add(gooodname);

        //sku价格集合
        List<SkuDTO> skus = spuDTO.getSkus();
        if(CollectionUtils.isEmpty(skus)){
            skus = itemClient.querySkuBySpuId(spuDTO.getId());
        }
        Set<Long> prices = skus.stream().map(SkuDTO::getPrice).collect(Collectors.toSet());

        //商品销量
        long sum = skus.stream().mapToLong(SkuDTO::getSold).sum();
        //sku的某个图片、
        String images = org.apache.commons.lang3.StringUtils.substringBefore(skus.get(0).getImages(), ",");
        //规格参数
        List<Map<String,Object>> spes=new ArrayList<>();
        List<SpecParamDTO> specParamDTOS = itemClient.querySpecsValues(spuDTO.getId(), true);
        for (SpecParamDTO specParamDTO : specParamDTOS) {
            Map<String,Object> map=new HashMap<>();
            map.put("name",specParamDTO.getName());
            map.put("value",chooseSegment(specParamDTO));
            spes.add(map);
        }

        Goods goods = new Goods();
        goods.setId(spuDTO.getId());
        goods.setTitle(gooodname+" "+brandName+" "+categoryName);
        goods.setUpdateTime(new Date());
        goods.setSuggestion(suggrsdt);
        goods.setPrices(prices);
        goods.setSold(sum);
        goods.setImage(images);
        goods.setSpecs(spes);
        goods.setBrandId(spuDTO.getBrandId());
        goods.setCategoryId(spuDTO.getCid3());
        return goods;
    }

    private Object chooseSegment(SpecParamDTO p) {
        Object value = p.getValue();
        if (value == null || org.apache.commons.lang3.StringUtils.isBlank(value.toString())) {
            return "其它";
        }
        if (!p.getNumeric() || org.apache.commons.lang3.StringUtils.isBlank(p.getSegments()) || value instanceof Collection) {
            return value;
        }
        double val = parseDouble(value.toString());
        String result = "其它";
        // 保存数值段
        for (String segment : p.getSegments().split(",")) {
            String[] segs = segment.split("-");
            // 获取数值范围
            double begin = parseDouble(segs[0]);
            double end = Double.MAX_VALUE;
            if (segs.length == 2) {
                end = parseDouble(segs[1]);
            }
            // 判断是否在范围内
            if (val >= begin && val < end) {
                if (segs.length == 1) {
                    result = segs[0] + p.getUnit() + "以上";
                } else if (begin == 0) {
                    result = segs[1] + p.getUnit() + "以下";
                } else {
                    result = segment + p.getUnit();
                }
                break;
            }
        }
        return result;
    }

    private double parseDouble(String str) {
        try {
            return Double.parseDouble(str);
        } catch (Exception e) {
            return 0;
        }
    }
    @Override
    public Mono<List<String>> getsuggest(String key) {
        if (StringUtils.isEmpty(key)) {
            throw new lyExcption(400, "参数不能为空");
        }
        return repository.suggestBySingleField("suggestion", key);
    }

    @Override
    public Mono<PageInfo<Goods>> getkist(SearchParamDTO searchParamDTO) {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        sourceBuilder.query(getquery(searchParamDTO));
        sourceBuilder.from(searchParamDTO.getFrom());
        sourceBuilder.size(searchParamDTO.getSize());
        String sortBy = searchParamDTO.getSortBy();

        HashSet<String> sorting = new HashSet<>();
        sorting.add("prices");
        sorting.add("sold");
        sorting.add("updateTime");
        if (!StringUtils.isEmpty(sortBy) && sorting.contains(sortBy)) {
            sourceBuilder.sort(sortBy, searchParamDTO.getDesc() ? SortOrder.DESC : SortOrder.ASC);
        }
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.preTags("<am>");
        highlightBuilder.postTags("</am>");
        highlightBuilder.field("title");
        sourceBuilder.highlighter(highlightBuilder);
        return repository.queryBySourceBuilderForPageHighlight(sourceBuilder);
    }

    @Override
    public Mono<Map<String, List<?>>> getfilter(SearchParamDTO searchParamDTO) {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        Mono<Aggregations> aggregation = repository.aggregationBySourceBuilder(sourceBuilder);
        sourceBuilder.query(getquery(searchParamDTO));
        sourceBuilder.size(0);
        sourceBuilder.aggregation(AggregationBuilders.terms("brandAgg").field("brandId"));
        sourceBuilder.aggregation(AggregationBuilders.terms("categoryAgg").field("categoryId"));
        sourceBuilder.aggregation(AggregationBuilders.nested("specsAgg", "specs")
                .subAggregation(AggregationBuilders.terms("nameAgg").field("specs.name")
                        .subAggregation(AggregationBuilders.terms("valueAgg").field("specs.value"))));


        //mono中，对每个元素进行挨个运算,对mono中的值进行二次封装
        return aggregation.map(aggregations -> {
            Map<String, List<?>> map = new LinkedHashMap<>();
            //缩小范围
            Terms brandAgg = aggregations.get("brandAgg");
            Terms categoryAgg = aggregations.get("categoryAgg");


            //取了那么久还是品牌名称
            List<Long> brandids = brandAgg.getBuckets().stream().map(bucket -> ((Terms.Bucket) bucket).getKeyAsNumber())
                    .map(Number::longValue).collect(Collectors.toList());

            List<Long> categoryids = categoryAgg.getBuckets().stream().map(buckets -> ((Terms.Bucket) buckets).getKeyAsNumber()).map(Number::longValue).collect(Collectors.toList());

            if (!CollectionUtils.isEmpty(brandids)) {
                map.put("品牌", itemClient.queryBrandByIds(brandids));
            }
            if (!CollectionUtils.isEmpty(categoryids)) {
                map.put("分类", itemClient.queryCategoryByIds(categoryids));
            }
            Nested specsAgg = aggregations.get("specsAgg");
            Terms nameAgg = specsAgg.getAggregations().get("nameAgg");
            nameAgg.getBuckets().forEach(buckets->{
                String ley = ((Terms.Bucket) buckets).getKeyAsString();
                Terms valueAgg = ((Terms.Bucket) buckets).getAggregations().get("valueAgg");
                List<String> valuelist = valueAgg.getBuckets().stream().map(bucket -> {
                    return ((Terms.Bucket) bucket).getKeyAsString();
                }).collect(Collectors.toList());
                    map.put(ley,valuelist);

            });

            return map;
        });

    }

    //上架
    @Override
    public void saveGoodsById(Long spuId) {
        PageDTO<SpuDTO> spuDto = itemClient.querySpuByPage(1, 1, true, null, null, spuId);
        if (spuDto.getItems().size()==1){
            repository.save(buildgoods(spuDto.getItems().get(0)));
        }

    }

    //下架
    @Override
    public void deleteGoodsById(Long spuId) {

        boolean b = repository.deleteById(spuId);
        System.out.println(b);
    }


    private static QueryBuilder getquery(SearchParamDTO searchParamDTO) {
        if (StringUtils.isEmpty(searchParamDTO.getKey())) {
            throw new lyExcption(400, "参数不能为空");
        }
        BoolQueryBuilder boolQueryBuilder=new BoolQueryBuilder();
        boolQueryBuilder.must(QueryBuilders.matchQuery("title", searchParamDTO.getKey()).operator(Operator.AND));
        if (!CollectionUtils.isEmpty(searchParamDTO.getFilters())){
            Set<Map.Entry<String, String>> entries = searchParamDTO.getFilters().entrySet();
            for (Map.Entry<String, String> entry : entries) {
                String querykey = entry.getKey();
                String queryValue = entry.getValue();
                if ("品牌".equals(querykey)){
                    boolQueryBuilder.filter(QueryBuilders.termQuery("brandId",queryValue));
                }else  if("分类".equals(querykey)){
                    boolQueryBuilder.filter(QueryBuilders.termQuery("categoryId",queryValue));
                }else {
                    BoolQueryBuilder subool=QueryBuilders.boolQuery();
                    subool.must(QueryBuilders.termQuery("specs.name",querykey));
                    subool.must(QueryBuilders.termQuery("specs.value",queryValue));
                    boolQueryBuilder.filter(QueryBuilders.nestedQuery("specs",subool, ScoreMode.None));
                }
            }

        }
        return boolQueryBuilder;
    }
}

